package com.aloudmeta.grpc;

import com.aloudmeta.grpc.demo.EchoServiceGrpc;
import com.aloudmeta.grpc.demo2.Proto2EchoServiceGrpc;
import com.aloudmeta.grpc.support.authentication.BasicGrpcAuthenticationReader;
import com.aloudmeta.grpc.support.authentication.CompositeGrpcAuthenticationReader;
import com.aloudmeta.grpc.support.authentication.GrpcAuthenticationReader;
import com.aloudmeta.grpc.support.check.AccessPredicate;
import com.aloudmeta.grpc.support.check.AccessPredicateVoter;
import com.aloudmeta.grpc.support.check.GrpcSecurityMetadataSource;
import com.aloudmeta.grpc.support.check.ManualGrpcSecurityMetadataSource;
import io.envoyproxy.pgv.ReflectiveValidatorIndex;
import io.envoyproxy.pgv.ValidatorIndex;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.access.AccessDecisionManager;
import org.springframework.security.access.AccessDecisionVoter;
import org.springframework.security.access.vote.UnanimousBased;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.AuthenticationProvider;
import org.springframework.security.authentication.ProviderManager;
import org.springframework.security.authentication.dao.DaoAuthenticationProvider;
import org.springframework.security.crypto.password.NoOpPasswordEncoder;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

/**
 * https://www.baeldung.com/spring-security-method-security
 * The prePostEnabled property enables Spring Security pre/post annotations.
 * The securedEnabled property determines if the @Secured annotation should be enabled.
 * The jsr250Enabled property allows us to use the @RoleAllowed annotation.
 *
 * huaixin 2022/1/23 3:24 PM
 */
@Configuration
public class MethodSecurityConfig
        /*extends GlobalMethodSecurityConfiguration*/ {

    @Bean
    AuthenticationManager authenticationManager() {
        final List<AuthenticationProvider> providers = new ArrayList<>();
        providers.add(daoAuthenticationProvider()); //JwtAuthenticationProvider (OAuth2/JWT/...)
        return new ProviderManager(providers);
    }

    @Bean
    ValidatorIndex validatorIndex() {
        return new ReflectiveValidatorIndex();
    }

    @Bean
    public AuthenticationProvider daoAuthenticationProvider() {
        DaoAuthenticationProvider provider = new DaoAuthenticationProvider();
        provider.setUserDetailsService(inMemoryUserDetailsManager());
        provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance());
        return provider;
    }

    @Bean
    public InMemoryUserDetailsManager inMemoryUserDetailsManager() {
        final Properties users = new Properties();
        users.put("melin", "000000,ROLE_USER,enabled");
        return new InMemoryUserDetailsManager(users);
    }

    @Bean
    public GrpcAuthenticationReader grpcAuthenticationReader() {
        final List<GrpcAuthenticationReader> readers = new ArrayList<>();
        // The actual token class is dependent on your spring-security library (OAuth2/JWT/...)
        readers.add(new BasicGrpcAuthenticationReader());
        return new CompositeGrpcAuthenticationReader(readers);
    }

    @Bean
    public GrpcSecurityMetadataSource grpcSecurityMetadataSource() {
        final ManualGrpcSecurityMetadataSource source = new ManualGrpcSecurityMetadataSource();
        source.set(EchoServiceGrpc.getEchoMethod(), AccessPredicate.authenticated());
        source.set(Proto2EchoServiceGrpc.getEchoMethod(), AccessPredicate.authenticated());
        source.setDefault(AccessPredicate.denyAll());
        return source;
    }

    @Bean
    public AccessDecisionManager accessDecisionManager() {
        final List<AccessDecisionVoter<?>> voters = new ArrayList<>();
        voters.add(new AccessPredicateVoter());
        return new UnanimousBased(voters);
    }
}
